
#include <cubefs/api/api.hpp>
#include <natives.h>
#include <decls.hpp>
#include <exceptions.hpp>

#include <cubefs/core/tools/types/dispatcher.hpp>

#define CUBEFS_SOURCE "scalar_list.hpp:" CUBEFS_AT

using namespace cubefs::api;
using namespace cubefs::tools;

using namespace std;

template <typename Functor>
void DispatchScalarTypedList(TypedListBase *list, Functor &functor) {
    if (ListOfBigInt::MyType() == list->type()) {
        ListOfBigInt* tl = static_cast<ListOfBigInt*>(list);
        functor(tl);
    }
    else {
        throw new TypeCastException(CUBEFS_SOURCE, "Unrecognized scalar list type: "+list->type()->name(), list->type(), NULL);
    }
}

struct GetElementFn {
    jlong handle_;
    jint idx_;

    GetElementFn(jint idx): idx_(idx){}

    template <typename List>
    void operator()(List *list) {
        handle_ = list->Get(idx_);
    }
};

JNIEXPORT jlong JNICALL METADATA1(ScalarList, GetTypedElement) (JNIEnv *env, jclass, jlong handle, jint index) {
    CUBEFS_EXC_BEGIN

    TypedListBase* list = Handle2Ptr<TypedListBase>(handle);

    GetElementFn fn(index);

    DispatchScalarTypedList(list, fn);

    return fn.handle_;

    CUBEFS_EXC_HANDLE
    return 0;
}

struct SetElementFn {
    jlong handle_;
    jint idx_;

    SetElementFn(jint idx, jlong handle): idx_(idx), handle_(handle) {}

    template <typename List>
    void operator()(List *list) {
        typedef typename List::Element Element;

        const Element e = (Element)(handle_);

        list->Set(idx_, e);
    }
};

JNIEXPORT void JNICALL METADATA1(ScalarList, SetTypedElement) (JNIEnv *env, jclass, jlong handle, jint index, jlong element) {
    CUBEFS_EXC_BEGIN

    TypedListBase* list = Handle2Ptr<TypedListBase>(handle);

    SetElementFn fn(index, element);

    DispatchScalarTypedList(list, fn);

    CUBEFS_EXC_HANDLE
}


struct AddElementFn {
    jlong handle_;
    jint idx_;

    AddElementFn(jint idx, jlong handle): idx_(idx), handle_(handle) {}

    template <typename List>
    void operator()(List *list) {
        typedef typename List::Element Element;

        const Element e = (Element)(handle_);

        list->Insert(idx_, e);
    }
};


JNIEXPORT void JNICALL METADATA1(ScalarList, AddTypedElement) (JNIEnv *env, jclass, jlong handle, jint index, jlong element) {
    CUBEFS_EXC_BEGIN

    TypedListBase* list = Handle2Ptr<TypedListBase>(handle);

    AddElementFn fn(index, element);

    DispatchScalarTypedList(list, fn);

    CUBEFS_EXC_HANDLE
}


#undef CUBEFS_SOURCE